﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Easy.Domain.Core.Specification;
using System.Linq;

namespace Easy.Domain.Core
{
    public interface IRepository<T> where T : class
    {
        IUnitOfWork UnitOfWork { get; }

        #region Command

        void Add(T item);

        /// <summary>
        /// 标记该实体的状态为 'Modified'。
        /// 注意：<para>
        /// 1. 标记的只是关于该实体，并不会影响到导航属性，非导航属性都会被更新，导航属性的更新则根据EF的自动跟踪来完成 </para>
        /// 2. 例如主键 Id = 1 的实体A已经存在上下文，新创建一个 Id = 1 的实体B，
        /// 当把B的状态修改为 'Modified' 时会出现异常，因为主键冲突，处理这种情况可以使用覆盖方法，Cover(A, B);
        /// </summary>
        void Update(T item);

        /// <summary>
        /// 修改实体部分属性
        /// </summary>
        /// <param name="item">实体</param>
        /// <param name="updatePropertys">需要修改的属性。注意：参数object为匿名对象 new{item.Propert1, item.Propert2, ..}</param>
        void Update(T item, Expression<Func<T, object>> updatePropertys);

        void Delete(T item);

        void Delete(Guid id);

        /// <summary>
        /// 标记该实体的状态为 'Unchanged'。
        /// 注意：例如主键 Id = 1 的实体A已经存在上下文，新创建一个 Id = 1 的实体B，
        /// 当把B的状态修改为 'Unchanged' 时会出现异常，因为主键冲突，处理这种情况可以使用覆盖方法，Cover(A, B);
        /// </summary>
        void Attach(T item);

        void Detach(T item);

        /// <summary>
        /// 覆盖存在于上下文实体 toOriginal 的当前值。注意：如果实体 toOriginal 尚未存在于上下文中，那么会出现异常。
        /// </summary>
        /// <param name="toOriginal">即将被覆盖的对象</param>
        /// <param name="formCurrent">数据来源</param>
        void Cover(T toOriginal, T formCurrent);

        #endregion

        #region Query

        /// <summary>
        /// 先会在上下文中查找，即使这些数据还没有保存到数据库，如果找到就返回结果；<para>
        /// 如果找不到才会到数据库中查找，找到之后会在上下文中缓存起来。</para>
        /// </summary>
        /// <param name="id">主键</param>
        T Find(Guid id);

        T Find(ISpecification<T> specification, bool isNoTracking = false);

        IQueryable<T> Find(Expression<Func<T, bool>> filter, bool isNoTracking = false);

        IQueryable<T> Matching(ISpecification<T> specification, bool isNoTracking = false);

        #endregion
    }
}
